package engine.time;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import creatures.Creature;

/**
 * This class implements the time system.
 * <p>
 * Objects have speeds, which affect how long it takes them to perform actions.
 * <p>
 * An object could potentially have multiple actions in a row if all other objects are "busy."
 * <p>
 * Action ordering is consistent, if more than one object is scheduled to act at the same time, the
 * fastest one goes first, then the second fastest, and so on until reaching the slowest.
 * 
 * @author Kyle_Solo
 */
public class Time {

	private long currentTime = 0;
	private List<TimeEntry> time = new ArrayList<TimeEntry>();

	/**
	 * This tells the time system that the creature will be busy for <code>amount</code> time units.
	 * This amount is cumulative, calling:<br>
	 * <code>act(10); <br>
	 * act(10);</code> <br>
	 * will mark the creature as busy for 20 more time units.
	 * <p>
	 * Busy creatures will not be returned by next until they are no longer busy.
	 * 
	 * @param creature
	 *            the creature that is acting
	 * @param amount
	 *            the amount of additional time that this creature will be busy
	 */
	public void act(Creature creature, int amount) {
		int index = getIndex(creature);
		if (index == -1) {
			throw new RuntimeException(
					"Creatures can't act if they haven't been added to the time system.");
		}
		TimeEntry creatureEntry = time.get(index);
		creatureEntry.add(amount);
	}

	/**
	 * Adds a creature to this time system.
	 * 
	 * @param creature
	 *            the creature to add to this time system
	 */
	public void add(Creature creature) {
		if (!time.contains(creature)) {
			time.add(new TimeEntry(creature, currentTime));
		}
	}

	/**
	 * Empties the time system, removing all creature data.
	 */
	public void clear() {
		time.clear();
	}

	/**
	 * @param creature
	 *            the creature to look for
	 * @return whether the time system contains this creature
	 */
	public boolean contains(Creature creature) {
		return getIndex(creature) != -1;
	}

	/**
	 * This gets the time that a creature will next be allowed to act.
	 * 
	 * @param creature
	 *            the creature that is acting
	 * @return the time when this creature will next act
	 */
	public long get(Creature creature) {
		int index = getIndex(creature);
		if (index == -1) {
			throw new RuntimeException(
					"Creatures can't act if they haven't been added to the time system.");
		}
		TimeEntry creatureEntry = time.get(index);
		return creatureEntry.getTime();
	}

	/**
	 * @param creature
	 * @return the index of this creature, or -1 if the time system does not contain it
	 */
	private int getIndex(Creature creature) {
		int index = -1;
		for (int i = 0; i < time.size(); i++) {
			if (time.get(i).getCreature() == creature) {
				index = i;
				break;
			}
		}
		return index;
	}

	/**
	 * @return the creature whose turn it is
	 */
	public Creature next() {
		Collections.sort(time);
		if (time.size() == 0) {
			return null;
		} else {
			// update the current time
			currentTime = time.get(0).getTime();
			return time.get(0).getCreature();
		}
	}

	/**
	 * Removes a creature from the time system. The creature will no longer recieve updates
	 * automatically.
	 * 
	 * @param c
	 *            the creature to remove
	 * @return true if the creature was removed from the list
	 */
	public boolean remove(Creature c) {
		int index = getIndex(c);
		return index == -1 || time.remove(index) != null;
	}

	/**
	 * This explicitly sets the time that a creature will next be allowed to act.
	 * 
	 * @param creature
	 *            the creature that is acting
	 * @param time
	 *            the time at which this creature will be allowed to act
	 */
	public void set(Creature creature, long time) {
		int index = getIndex(creature);
		if (index == -1) {
			throw new RuntimeException(
					"Creatures can't act if they haven't been added to the time system.");
		}
		TimeEntry creatureEntry = this.time.get(index);
		creatureEntry.setTime(time);
	}

	@Override
	public String toString() {
		String r = "[ ";
		for (int i = 0; i < time.size(); i++) {
			r += time.get(i).toString();
			if (i + 1 != time.size()) {
				r += ",\n";
			}
		}
		r += " ]";
		return r;
	}
}